/* 
 * Copyright (c) 2012, Fromentin Xavier, Schnell Michaël, Dervin Cyrielle, Brabant Quentin
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *      * The names of its contributors may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Fromentin Xavier, Schnell Michaël, Dervin Cyrielle OR Brabant Quentin 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package kameleon.gui.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import kameleon.exception.KameleonException;
import kameleon.gui.exception.UnknownKeyException;
import kameleon.gui.language.SwitchLanguage;
import kameleon.gui.model.FileInfo;
import kameleon.gui.model.GenerationModel;
import kameleon.gui.model.Model;
import kameleon.gui.model.Observer;
import kameleon.gui.util.LanguageConstants;

/**
 * View showing the recently added files as well as the currently 
 * selected file. Also offers the means to add or delete files from
 * the history (= recently used) list.
 * 
 * @author		Fromentin Xavier, Schnell Michaël
 * @version		1.0
 */
public class HistoryView extends JPanel 
implements Observer, LanguageConstants {

	/**
	 * Needed to serialize this class.
	 * 
	 * @see		java.io.Serializable
	 */
	private static final long serialVersionUID = 3914306504859458701L ;
	
	/**
	 * Font used by plain text.
	 */
	private final Font PLAIN = new Font(
			this.getFont().getName(), Font.PLAIN, this.getFont().getSize()) ;
	
	/**
	 * Font used by italic text.
	 */
	private final Font ITALIC = new Font(
			this.getFont().getName(), Font.ITALIC, this.getFont().getSize()) ;

	/**
	 * Model of this view.
	 */
	protected Model model ;

	/**
	 * Component containing this view.
	 */
	protected Component container ;
	
	/**
	 * List of the recently added/used files.
	 */
	protected JList recentFiles ;
	
	/**
	 * Dialog used to add new files.
	 */
	protected JFileChooser fileChooser ;
	
	/**
	 * Path of the currently selected file.
	 */
	protected JTextField path ;
	
	/**
	 * Button used to add new files.
	 */
	protected JButton browse ;
	
	/**
	 * Adapter used by the list of recently added/used files.
	 */
	protected DefaultListModel adapter ;
	
	/**
	 * Contextual menu for the item of the list. USed to remove files
	 * from the list
	 */
	protected JPopupMenu popupMenu ;
	
	/**
	 * Menu item used to remove files from the list of recently added/used files.
	 */
	protected JMenuItem delete;

	/**
	 * Sole constructor.
	 * 
	 * @param	model
	 * 			model of this view
	 * 
	 * @param	container
	 * 			parent frame of this instance
	 * 
	 * @throws	KameleonException
	 * 			if an error occurred while building this instance
	 */
	public HistoryView(Model model, Component container)
			throws KameleonException {
		super() ;
		this.model = model ;
		this.model.addObserver(this) ;
		this.container = container ;
		this.fileChooser = new JFileChooser() ;
		this.build() ;
	}// HistoryView(Model, Component)

	/**
	 * Builds the content of this view.
	 * 
	 * @throws	UnknownKeyException
	 * 			if a key for a displayed text could not be found
	 */
	protected void build() throws UnknownKeyException {
		final SwitchLanguage sl = SwitchLanguage.getInstance() ;

		this.setLayout(new BorderLayout()) ;
		this.setBorder(BorderFactory.createTitledBorder(
				sl.getText(LanguageConstants.HISTORY_TITLE_BORDER))) ;

		JPanel north = new JPanel() ;
		north.setLayout(new BoxLayout(north, BoxLayout.X_AXIS)) ;

		this.path = new JTextField(sl.getText(LanguageConstants.NO_FILE)) ;
		this.path.setFont(this.ITALIC) ;
		this.path.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)) ;
		north.add(this.path) ;

		this.browse = new JButton(
				sl.getText(LanguageConstants.BROWSE_BUTTON)) ;
		this.browse.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				int browseResult = HistoryView.this.fileChooser.
						showOpenDialog(HistoryView.this.container) ;
				if (browseResult == JFileChooser.APPROVE_OPTION) {
					HistoryView.this.model.addFile(
							HistoryView.this.fileChooser.getSelectedFile()) ;
				}// if
			}// actionPerformed(ActionEvent)
		}) ;
		north.add(this.browse) ;
		this.add(north, BorderLayout.NORTH) ;

		this.adapter = new RecentFiles(this.model.getRecentFileInfo()) ;
		this.recentFiles = new JList(this.adapter) ;
		this.recentFiles.addListSelectionListener(new FileSelectionListener()) ;
		this.recentFiles.setSelectionMode(ListSelectionModel.SINGLE_SELECTION) ;
		this.recentFiles.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent me) {
				// if right mouse button clicked (or me.isPopupTrigger())
				if (SwingUtilities.isRightMouseButton(me)
						&& !HistoryView.this.recentFiles.isSelectionEmpty()
						&& HistoryView.this.recentFiles.locationToIndex(me.getPoint())
						== HistoryView.this.recentFiles.getSelectedIndex()) {
					HistoryView.this.popupMenu.show(
							HistoryView.this.recentFiles, me.getX(), me.getY()) ;
				}// if
			}// mouseClicked(MouseEvent)
		}) ;
		
		JScrollPane scroll = new JScrollPane(this.recentFiles) ;
		scroll.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)) ;
		this.add(scroll, BorderLayout.CENTER) ;
		
		this.delete = new JMenuItem(sl.getText(LanguageConstants.DELETE_BUTTON)) ;
		this.delete.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				HistoryView.this.model.deleteCurrentSelection() ;
			}// actionPerformed(ActionEvent)
		}) ;
		this.popupMenu = new JPopupMenu() ;
		this.popupMenu.setBorder(new LineBorder(Color.LIGHT_GRAY)) ;
		this.popupMenu.add(this.delete) ;
	}// build()

	/**
	 * Updates the displayed path for the currently selected file.
	 * 
	 * @throws 	UnknownKeyException
	 * 			if an error occurred while updating the text of this view
	 */
	@Override
	public void update() throws UnknownKeyException {
		// A file was selected, update displayed path
		if (this.model.fileIsSelected()) {
			if (this.model.selectionHasChanged()) {
				this.recentFiles.setSelectedValue(
						this.model.getSelectedFileInfo().getPath(), true) ;
			}
			this.path.setText(this.model.getSelectedFileInfo().getPath()) ;
			this.path.setFont(this.PLAIN) ;
		}// No file is selected, display default text
		else {
			this.path.setText(SwitchLanguage.getInstance().getText(
					LanguageConstants.NO_FILE)) ;
			this.path.setFont(this.ITALIC) ;
		}// if
	}// update()

	/**
	 * Updates the text to match the currently selected language.
	 * 
	 * @throws 	UnknownKeyException
	 * 			if an error occurred while updating the text of this view
	 */
	@Override
	public void reloadLanguage() throws UnknownKeyException {
		SwitchLanguage sl = SwitchLanguage.getInstance() ;
		
		TitledBorder border = (TitledBorder) this.getBorder() ;
		border.setTitle(sl.getText(LanguageConstants.HISTORY_TITLE_BORDER)) ;
		this.browse.setText(sl.getText(LanguageConstants.BROWSE_BUTTON)) ;
		this.fileChooser.setApproveButtonText(sl.getText(LanguageConstants.ADD_FILE)) ;
		if (!this.model.fileIsSelected()) {
			this.path.setText(sl.getText(LanguageConstants.NO_FILE)) ;
		}/// if
		this.delete.setText(sl.getText(LanguageConstants.DELETE_BUTTON)) ;
	}// reloadLanguage()

	/**
	 * Model handling the data for the list of recently added/used files.
	 * 
	 * @author	Fromentin Xavier, Schnell Michaël
	 * @version	1.0
	 */
	class RecentFiles extends DefaultListModel implements Observer {
		
		/**
		 * Needed to serialize this class.
		 * 
		 * @see		java.io.Serializable
		 */
		private static final long serialVersionUID = -5071053784542177604L;

		/**
		 * Sole constructor.
		 * 
		 * @param 	files
		 * 			initial files contained by the list
		 */
		public RecentFiles(FileInfo[] files) {
			super() ;
			for(FileInfo file : files) {
				this.addElement(file.getPath()) ;
			}// for
			HistoryView.this.model.addObserver(this) ;
		}// RecentFiles(FileInfo[])

		/**
		 * Updates the list of recently used/added files.
		 */
		@Override
		public void update() {
			GenerationModel gModel = HistoryView.this.model ;
			if (gModel.newFileAdded()) {
				FileInfo fileInfo = gModel.getSelectedFileInfo() ;
				this.add(0, fileInfo.getPath()) ;
				HistoryView.this.recentFiles.setSelectedIndex(0) ;
				this.fireContentsChanged(this, 0, this.getSize()-1) ;
			} else if (gModel.fileRemoved()){
				this.remove(gModel.getDeletedIndex()) ;
			}// if
		}// update()

		/**
		 * Does nothing, only file paths are displayed.
		 */
		@Override
		public void reloadLanguage() {/* No text to update. */}

	}// class RecentFiles

	/**
	 * Listener handling the clicks on the list items. Selects
	 * or "de-selects" the file displayed by the clicked list item.
	 * 
	 * @author	Fromentin Xavier, Schnell Michaël
	 * @version	1.0
	 */
	class FileSelectionListener implements ListSelectionListener {

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void valueChanged(ListSelectionEvent e) {
			if (!HistoryView.this.model.selectionHasChanged()
					&& !HistoryView.this.model.newFileAdded()) {
				if (!e.getValueIsAdjusting()) {
					int indice = HistoryView.this.recentFiles.getSelectedIndex() ;
					if (indice != -1) {
						HistoryView.this.model.selectFile(
								(String) HistoryView.this.adapter.get(indice)) ;
					} else {
						HistoryView.this.model.clearSelection() ;
					}// if
				}// if
			}// if
		}// valueChanged(ListSelectionEvent)
	
	}// class FileSelectionListener

}// class HistoryView